"""
Copyright 2012 Wieser Dietmar, Margit Mutschlechner.

This file is part of Multimedia Social Networking Platform.

Multimedia Social Networking Platform is free software: 
you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

Multimedia Social Networking Platform is distributed
in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with Multimedia Social Networking Platform.  
If not, see <http://www.gnu.org/licenses/>.

"""

import Pyro.core
from msnp.service.network.model.Model import ConnectionType
from msnp.service.network import model
from msnp.service.network import logic
from msnp.service import network
import random
import traceback
from msnp.logger import Logger
from msnp.service.utils import Hash
from msnp.persistence import XMLController
from msnp.gui import MySignal
from msnp.service.network import networktimer
from msnp.service.network.networktimer import KeepAliveTimer
from msnp.model import PTPModel

"""Module for initiating connections to a remote peer and calling remote functions there.
Contains also a private function to initialize a connection.""" 


def __initConnection(ip, port, type):
    """This function initiates a connection to a given URI.
    
    It puts the arguments together to a URI and returns a proxy for that URI.
    
    Args:
        ip: the ip (string)
        port: the port (integer)
        type: the type (string)
    
    Returns:
        a proxy for the constructed URI
    """
    
    proxy = Pyro.core.getProxyForURI("PYROLOC://" + ip + ":" + str(port) + "/" + type)
    proxy._setTimeout(5)
    return proxy


####################
## AUTHENTICATION ##
####################

def authenticate(responsibleIdentity, isLogin, sendCount=3):
    """This function executes a remote procedure call to the authenticate function (RemoteModel.Authentication).
    
    Args:
        responsibleIdentity: The responsible identity (PeerInfo instance)
        isLogin: This boolean determines whether the peer initiates a login or a registration.
        sendCount: Maximum number of retries.
    """
    
    try:
        if responsibleIdentity != None:
            sendCount -= 1
            Logger.info('Authenticate to responsible identity: ' + responsibleIdentity.getNameBundle())
            port = responsibleIdentity.getPortForTag(ConnectionType.AUTHENTICATION)
            if port != None:
                auth = __initConnection(responsibleIdentity.getIp(), port, ConnectionType.AUTHENTICATION)
                auth.authenticate(network.NetworkController.getMySelf().getMyIdentity(), isLogin)
            else:
                Logger.info('port none in authenticate')
        else:
            Logger.info('responsibleIdentity is none in authenticate')
    except:
        Logger.info("error while authenticate in sender. count: " + str(sendCount))
        #no crash message because i am not in the ring yet
        if sendCount > 0:
            networktimer.ResendTimer.addCall([authenticate, responsibleIdentity, isLogin, sendCount])
        else:
            hello(isLogin, None)


###########################
## RESPONSIBILITY_SEARCH ##
###########################

def forwardResponsibilityCallForAuth(searchedName, superPeerIdentity, isLogin, crashSend=False):
    """This function executes a remote procedure call to the findResponsibleIdentityForAuth function (RemoteModel.ResponsibilitySearch).
    
    Args:
        searchedName: The searched name (string)
        superPeerIdentity: the super peer identity (PeerInfo instance)
        isLogin: a boolean determining whether the executed authentication procedure is a registration or a login.
        crashSend: a boolean determining whether a crash is already executed.
    """
    
    searchedHashedNameLong = long(Hash.hash(searchedName), 16)
    nearestIdentity = network.NetworkController.getMySelf().getNearestIdentity(searchedHashedNameLong)[0]
    try:
        if nearestIdentity != None:
            port = nearestIdentity.getPortForTag(ConnectionType.RESPONSIBILITY_SEARCH)
            if port != None:
                respSearch = __initConnection(nearestIdentity.getIp(), port, ConnectionType.RESPONSIBILITY_SEARCH)
                respSearch.findResponsibleIdentityForAuth(searchedName, superPeerIdentity, isLogin)
            else:
                Logger.info('port is None in forwardResponibilityCallForAuth')
        else:
            Logger.info('nearestIdentity is None in forwardResponibilityCallForAuth')
    except:
        Logger.info('exception while searching responsible identity for authentication in sender')
        if crashSend == False:
            logic.CrashLogic.handleCrash(nearestIdentity)
        networktimer.ResendTimer.addCall([forwardResponsibilityCallForAuth, searchedName, superPeerIdentity, isLogin, True])

        
def callbackResponsibleIdentityForAuth(identity, superPeerIdentity, receiverName, isLogin, sendCount=3):
    """This function executes a remote procedure call to the callbackResponsibleIdentityForAuth function (RemoteModel.ResponsibilitySearch).
    
    Args:
        identity: PeerInfo instance
        receiverName: the receiver name (string)
        superPeerIdentity: the super peer identity (PeerInfo instance)
        isLogin: a boolean determining whether the executed authentication procedure is a registration or a login.
        sendCount: Maximum number of retries.
    """
    
    try:
        if superPeerIdentity != None:
            sendCount -= 1
            port = superPeerIdentity.getPortForTag(ConnectionType.RESPONSIBILITY_SEARCH)
            if port != None:
                respSearch = __initConnection(superPeerIdentity.getIp(), port, ConnectionType.RESPONSIBILITY_SEARCH)
                respSearch.callbackResponsibleIdentityForAuth(identity, receiverName, isLogin)
            else:
                Logger.info('port none in callbackResponsibleIdentityForAuth')
        else:
            Logger.info('superPeerIdentity is none in callbackResponsibleIdentityForAuth')
    except:
        Logger.info("error while callback responsible identity for authentication in sender. count: " + str(sendCount))
        if sendCount == 0:
            logic.CrashLogic.handleCrash(superPeerIdentity)
        if sendCount > 0:
            networktimer.ResendTimer.addCall([callbackResponsibleIdentityForAuth, identity, superPeerIdentity, receiverName, isLogin, sendCount])
        traceback.print_exc()


def forwardResponsibilityCall(receiverName, senderName, senderRespIdentity, reactType, crashSend=False):
    """This function executes a remote procedure call to the findResponsibleIdentity function (RemoteModel.ResponsibilitySearch).
    
    Args:
        receiverName: the receiver name (string)
        senderName: the sender name (string)
        senderRespIdentity: the responsible identity of the sender (PeerInfo instance)
        reactType: the react type is forwarded to other functions
        crashSend: a boolean determining whether a crash is already executed.
    """

    receiverHashedNameLong = long(Hash.hash(receiverName), 16)
    nearestIdentity = network.NetworkController.getMySelf().getNearestIdentity(receiverHashedNameLong)[0]
    try:
        if nearestIdentity != None:
            port = nearestIdentity.getPortForTag(ConnectionType.RESPONSIBILITY_SEARCH)
            if port != None:
                respSearch = __initConnection(nearestIdentity.getIp(), port, ConnectionType.RESPONSIBILITY_SEARCH)
                respSearch.findResponsibleIdentity(receiverName, senderName, senderRespIdentity, reactType)
            else:
                Logger.info('port is None in forwardResponibilityCall')
        else:
            Logger.info('nearestIdentity is None in forwardResponibilityCall')
    except:
        Logger.info('exception while searching responsible identity in sender. send to: ' + nearestIdentity.getName() + ", ip=" + nearestIdentity.getIp() + ", port=" + str(nearestIdentity.getPortForTag(ConnectionType.RESPONSIBILITY_SEARCH)))
        if crashSend == False:
            logic.CrashLogic.handleCrash(nearestIdentity)
        networktimer.ResendTimer.addCall([forwardResponsibilityCall, receiverName, senderName, senderRespIdentity, reactType, True])


def callbackResponsibleIdentity(receiverRespIdentity, senderRespIdentity, senderName, receiverName, result, reactType, sendCount=3):
    """This function executes a remote procedure call to the callbackResponsibleIdentity function (RemoteModel.ResponsibilitySearch).
    
    Args:
        receiverRespIdentity: the responsible identity of the receiver (PeerInfo instance)
        senderRespIdentity: the resonsible identity of the sender (PeerInfo instance)
        senderName: the sender name (string)
        receiverName: the receiver name (string)
        result: the result is forwarded to other functions
        reactType: the react type is forwarded to other functions
        sendCount: Maximum number of retries.
    """
    
    try:
        if senderRespIdentity != None:
            sendCount -= 1
            port = senderRespIdentity.getPortForTag(ConnectionType.RESPONSIBILITY_SEARCH)
            if port != None:
                respSearch = __initConnection(senderRespIdentity.getIp(), port, ConnectionType.RESPONSIBILITY_SEARCH)
                respSearch.callbackResponsibleIdentity(receiverRespIdentity, senderName, receiverName, result, reactType)
            else:
                Logger.info('port none in callbackResponsibleIdentity')
        else:
            Logger.info('senderRespIdentity is none in callbackResponsibleIdentity')
    except:
        Logger.info("error while callback responsible identity in sender. count: " + str(sendCount))
        if sendCount == 0:
            logic.CrashLogic.handleCrash(senderRespIdentity)
        if sendCount > 0:
            networktimer.ResendTimer.addCall([callbackResponsibleIdentity, receiverRespIdentity, senderRespIdentity, senderName, receiverName, result, reactType, sendCount])

    
def callbackResponsibleIdentityForFinger(identity, originalIdentity, lowerFingerIndex, upperFingerIndex, sendCount=3):
    """This function executes a remote procedure call to the callbackResponsibleIdentityForFinger function (RemoteModel.ResponsibilitySearch).
    
    Args:
        identity: the received finger candidate (PeerInfo instance)
        originalIdentity: the receiver of the call (PeerInfo instance)
        lowerFingerIndex: the lower finger index (inclusive - integer)
        upperFingerIndex: the upper finger index (inclusive - integer)
        sendCount: Maximum number of retries.
    """
    
    try:
        if originalIdentity != None:
            sendCount -= 1
            port = originalIdentity.getPortForTag(ConnectionType.RESPONSIBILITY_SEARCH)
            if port != None:
                respSearch = __initConnection(originalIdentity.getIp(), port, ConnectionType.RESPONSIBILITY_SEARCH)
                respSearch.callbackResponsibleIdentityForFinger(identity, lowerFingerIndex, upperFingerIndex)
            else:
                Logger.info('port none in callbackResponsibleIdentityForFinger')
        else:
            Logger.info("originalIdentity is none in callbackResponsibleIdentityForFinger")
    except:
        Logger.info("error while callback responsible identity for finger in sender. count: " + str(sendCount))
        if sendCount == 0:
            logic.CrashLogic.handleCrash(originalIdentity)
        if sendCount > 0:
            networktimer.ResendTimer.addCall([callbackResponsibleIdentityForFinger, identity, originalIdentity, lowerFingerIndex, upperFingerIndex, sendCount])


def findPredecessorOfCrashedIdentity(crashedIdentity, alreadyReceivedUsers):
    """This function executes a remote procedure call to the findPredecessorOfCrashedIdentity function (RemoteModel.ResponsibilitySearch).
    
    Args:
        crashedIdentity: the crashed identity (PeerInfo instance).
        alreadyReceivedUsers: a list containing peers that already received the message.
    """
    
    try:
        nearestIdentityForCrash = network.NetworkController.getMySelf().getNearestIdentityForCrash(crashedIdentity.getHashedNameLong())
        if nearestIdentityForCrash != None: 
            port = nearestIdentityForCrash.getPortForTag(ConnectionType.RESPONSIBILITY_SEARCH)
            if port != None:
                Logger.info("forward search predecessor for " + crashedIdentity.getName() + " to " + nearestIdentityForCrash.getName())
                respSearch = __initConnection(nearestIdentityForCrash.getIp(), port, ConnectionType.RESPONSIBILITY_SEARCH)
                respSearch.findPredecessorOfCrashedIdentity(crashedIdentity, alreadyReceivedUsers)
            else:
                Logger.info('port none in findPredecessorOfCrashedIdentity')
        else:
            Logger.info("nearestIdentityForCrash none in findPredecessorOfCrashedIdentity")
    except:
        Logger.info("error while find predecessor of crashed identity in sender -> " + nearestIdentityForCrash.getName())
        logic.CrashLogic.handleCrash(nearestIdentityForCrash)


#################
## FINGERTABLE ##
#################

def updatePredecessorOfSuccessor(predecessor, newSuccessor, isCrash=False):
    """This function executes a remote procedure call to the updatePredecessorOfSuccessor function (RemoteModel.Fingertable).
    
    Args:
        predecessor: the predecessor (PeerInfo instance)
        newSuccessor: the receiver of the call (PeerInfo instance)
        isCrash: This boolean determines if an underlying crash procedure is executed.
        
    Returns:
        Returns SUCCESSFUL, RECONNECT, FORCE_REHELLO, ERROR or UPDATE_PREDECESSOR_ERROR.
    """
    
    try:
        if newSuccessor != None:
            port = newSuccessor.getPortForTag(ConnectionType.FINGERTABLE)
            if port != None:
                netUpdate = __initConnection(newSuccessor.getIp(), port, ConnectionType.FINGERTABLE) 
                return netUpdate.updatePredecessorOfSuccessor(predecessor, isCrash)
            else:
                Logger.info('port none in updatePredecessorOfSuccessor')
        else:
            Logger.info('successor is None in updatePredecessorOfSuccessor')
    except:
        Logger.info("error while updating predecessor of successor in sender. newSuccessor= " + newSuccessor.getName())
        Logger.info(traceback.format_exc())
        logic.CrashLogic.handleCrash(newSuccessor)
    return PTPModel.ReturnType.ERROR

        
def getSuccessor(identity):
    """This function executes a remote procedure call to the getSuccessor function (RemoteModel.Fingertable).
    
    Args:
        identity: the receiver of the call (PeerInfo instance)
        
    Returns:
        Returns the successor as PeerInfo instance, None if an error occurred.
    """
    
    try:
        if identity != None:
            port = identity.getPortForTag(ConnectionType.FINGERTABLE)
            if port != None:
                netUpdate = __initConnection(identity.getIp(), port, ConnectionType.FINGERTABLE) 
                return netUpdate.getSuccessor()
            else:
                Logger.info('port none in getSuccessor')
        else:
            Logger.info("identity none in getSuccessor")
    except:
        Logger.info("error while getting successor in sender")
    return None


#################
## PEER_UPDATE ##
#################

def superPeerAbilityTest():
    """This function executes a remote procedure call to the superPeerAbilityTest function (RemoteModel.PeerUpdate).
    """
    
    try:
        respIdentity = network.NetworkController.getMySelf().getResponsibleSuperPeer()
        if respIdentity != None:
            port = respIdentity.getPortForTag(ConnectionType.PEERUPDATE)
            if port != None:
                myIdentity = network.NetworkController.getMySelf().getMyIdentity()
                netUpdate = __initConnection(respIdentity.getIp(), port, ConnectionType.PEERUPDATE) 
                netUpdate.superPeerAbilityTest(myIdentity)
            else:
                Logger.info('port none in superpeerabilitytest')
        else:
            Logger.info('responsible identity none in superpeerabilitytest')
    except:
        Logger.info("error while testing for super peer ability in sender")
        #put into queue to resend, somewhen the responsible peer has to be reachable again
        networktimer.ResendTimer.addCall([superPeerAbilityTest])
    

def sendShiftedData(receiverIdentity, llPeerList, messages):
    """This function executes a remote procedure call to the sendShiftedData function (RemoteModel.PeerUpdate).
    
    Args:
        receiverIdentity: the receiver of the call (PeerInfo instance)
        llPeerList: A list of low level peers (PeerInfo instances).
        messages: a list of CustodyMessage instances.
        
    Returns:
        The return is defined by ReturnType and be one of the following values: SUCCESSFUL or ERROR
    """
    
    try:
        if receiverIdentity != None:
            port = receiverIdentity.getPortForTag(ConnectionType.PEERUPDATE)
            if port != None:
                netUpdate = __initConnection(receiverIdentity.getIp(), port, ConnectionType.PEERUPDATE) 
                return netUpdate.sendShiftedData(llPeerList, messages)
            else:
                Logger.info('port none in sendShiftedData')
        else:
            Logger.info('receiverIdentity is None in sendShiftedData')
    except:
        Logger.info("error while sending shifted data in sender")
    return PTPModel.ReturnType.ERROR
        
        
def joinRing(newSuperPeer, predecessor):
    """This function executes a remote procedure call to the joinRing function (RemoteModel.PeerUpdate).
    
    Args:
        newSuperPeer: the receiver of the call (PeerInfo instance)
        predecessor: the predecessor (PeerInfo instance)
        
    Returns:
        The return is defined by ReturnType and be one of the following values: SUCCESSFUL or ERROR
    """
    
    try:
        if newSuperPeer != None:
            newSuccessor = network.NetworkController.getMySelf().getMyIdentity()
            port = newSuperPeer.getPortForTag(ConnectionType.PEERUPDATE)
            if port != None:
                netUpdate = __initConnection(newSuperPeer.getIp(), port, ConnectionType.PEERUPDATE) 
                return netUpdate.joinRing(newSuccessor, predecessor)
            else:
                Logger.info('port none in joinRing')
        else:
            Logger.info('newSuperPeer is none in joinRing')
    except:
        Logger.info("error while joining ring in sender")
        Logger.info(traceback.format_exc())
    return PTPModel.ReturnType.ERROR

    
def logoutLowLevelPeer(sendCount=3):
    """This function executes a remote procedure call to the logoutLowLevelPeer function (RemoteModel.PeerUpdate).
    
    Args:
        sendCount: Maximum number of retries.
        
    Returns:
        False
    """
    
    try:
        sendCount -= 1
        superPeer = network.NetworkController.getMySelf().getResponsibleSuperPeer()
        myName = network.NetworkController.getMySelf().getMyIdentity().getName()
        if superPeer != None:
            port = superPeer.getPortForTag(ConnectionType.PEERUPDATE)
            if port != None:
                netUpdate = __initConnection(superPeer.getIp(), port, ConnectionType.PEERUPDATE) 
                return netUpdate.logoutLowLevelPeer(myName)
            else:
                Logger.info('port none in forwardFinishCrash')
        else:
            Logger.info('superPeer is none in forwardFinishCrash')
    except:
        Logger.info('error while logging out low level peer in sender. count: ' + str(sendCount))
        if sendCount > 0:
            networktimer.ResendTimer.addCall([logoutLowLevelPeer, sendCount])
    return False
    

def getTimestamp(identity):
    """This function executes a remote procedure call to the getTimestamp function (RemoteModel.PeerUpdate).
    
    Returns:
        Returns the current time stamp as float, None if an error occurred.
    """
    
    try:
        if identity != None:
            port = identity.getPortForTag(ConnectionType.PEERUPDATE)
            if port != None:
                peerUpdate = __initConnection(identity.getIp(), port, ConnectionType.PEERUPDATE)
                return peerUpdate.getTimestamp()
            else:
                Logger.info("port none in gettimestamp in sender")
        else:
            Logger.info("identity is None in getTimestamp in sender")
    except:
        Logger.info("error while getting timestamp in sender")
    return None


def sendMessages(identity, queueMessages):
    """This function executes a remote procedure call to the sendMessages function (RemoteModel.PeerUpdate).
    
    Args:
        identity: the receiver of the call (PeerInfo instance)
        queueMessages: The list of CustodyMessage instances.
    """
    
    try:
        if identity != None:
            port = identity.getPortForTag(ConnectionType.PEERUPDATE)
            if port != None:
                peerUpdate = __initConnection(identity.getIp(), port, ConnectionType.PEERUPDATE)
                peerUpdate.sendMessages(queueMessages)
            else:
                Logger.info("port none in gettimestamp in sender")
        else:
            Logger.info("identity is None in getTimestamp in sender")
    except:
        Logger.info("error while sending messages in sender")
    

############
## BACKUP ##
############

def sendBackup(peer, backupContent, recognizeCrash=True):
    """This function executes a remote procedure call to the sendBackup function (RemoteModel.Backup).
    
    Args:
        peer: the receiver of the call (PeerInfo instance)
        backupContent: The BackupContent instance.
        recognizeCrash: a boolean determining whether a crash is already executed.
        
    Returns:
        The return is defined by ReturnType and can be one of the following values: SUCCESSFUL or ERROR
    """
    
    returnType = PTPModel.ReturnType.ERROR
    try:
        if peer != None:
            port = peer.getPortForTag(ConnectionType.BACKUP)
            if port != None:
                netUpdate = __initConnection(peer.getIp(), port, ConnectionType.BACKUP)
                netUpdate.sendBackup(backupContent)
                return PTPModel.ReturnType.SUCCESSFUL
            else:
                Logger.info('port none in sendBackup')
        else:
            Logger.info('peer is none in sendBackup')
    except:
        Logger.info("error while sending backup in sender")
        if recognizeCrash:
            logic.CrashLogic.handleCrash(peer)
    return returnType


def backupCallback(callbackIdentity, backups, sendCount=1):
    """This function executes a remote procedure call to the backupCallback function (RemoteModel.Backup).
    
    Args:
        callbackIdentity: the receiver of the call (PeerInfo instance)
        backups: The list of BackupContent instances.
        sendCount: Maximum number of retries.
    """
    
    try:
        sendCount -= 1
        if callbackIdentity != None:
            port = callbackIdentity.getPortForTag(ConnectionType.BACKUP)
            if port != None:
                netUpdate = __initConnection(callbackIdentity.getIp(), port, ConnectionType.BACKUP)
                netUpdate.backupCallback(backups)
                Logger.info("backupCallback to " + callbackIdentity.getName())
            else:
                Logger.info('port none in backupCallback')
        else:
            Logger.info('callbackIdentity is none in backupCallback')
    except:
        Logger.info("error while callbacking backup in sender. count: " + str(sendCount))
        if sendCount == 0:
            logic.CrashLogic.handleCrash(callbackIdentity)
        if sendCount > 0:
            networktimer.ResendTimer.addCall([backupCallback, callbackIdentity, backups, sendCount])


###########
## CRASH ##
###########

def sendRTMessageErrorCallback(identity, rtMessage, sendCount=3):
    """This function executes a remote procedure call to the rtMessageErrorCallback function (RemoteModel.Crash).
    
    Args:
        identity: the receiver of the call (PeerInfo instance)
        rtMessage: The ReliableTransportMessage instance.
        sendCount: Maximum number of retries.
    """
    
    try:
        sendCount -= 1
        if identity != None:
            port = identity.getPortForTag(ConnectionType.CRASH)
            if port != None:
                m = __initConnection(identity.getIp(), port, ConnectionType.CRASH)
                m.rtMessageErrorCallback(rtMessage)
            else:
                Logger.info('port none in sendRTMessageErrorCallback')
        else:
            Logger.info('identity is none in sendRTMessageErrorCallback')
    except:
        Logger.info("error in sendRTMessageErrorCallback")
        Logger.info(traceback.format_exc())
        if sendCount == 0:
            logic.CrashLogic.handleCrash(identity)
        if sendCount > 0:
            networktimer.ResendTimer.addCall([sendRTMessageErrorCallback, identity, rtMessage, sendCount])
        
        
def sendReconnectRing(identity, newSuccessor, rtMessage, sendCount=3):
    """This function executes a remote procedure call to the reconnectRing function (RemoteModel.Crash).
    
    Args:
        identity: the receiver of the call (PeerInfo instance)
        neSuccessor: The new successor (PeerInfo instance).
        rtMessage: The ReliableTransportMessage instance.
        sendCount: Maximum number of retries.
    """
    
    try:
        sendCount -= 1
        if identity != None:
            port = identity.getPortForTag(ConnectionType.CRASH)
            if port != None:
                m = __initConnection(identity.getIp(), port, ConnectionType.CRASH)
                m.reconnectRing(newSuccessor, rtMessage)
            else:
                Logger.info('port none in sendReconnectRing')
        else:
            Logger.info('identity is none in sendReconnectRing')
    except:
        Logger.info("error in sendReconnectRing")
        if sendCount == 0:
            logic.CrashLogic.handleCrash(identity)
        if sendCount > 0:
            networktimer.ResendTimer.addCall([sendReconnectRing, identity, newSuccessor, rtMessage, sendCount])


def sequentialDelivery(rtMsg, originalIdentity, alreadyReceivedUsers):
    """This function executes a remote procedure call to the sequentialDelivery function (RemoteModel.Crash).
    
    Args:
        rtMsg: The ReliableTransportMessage instance.
        originalIdentity: The initiator of the message (PeerInfo instance)
        alreadyReceivedUsers: A list of peers that already received the message.
    """
    
    try:
        #send it backward because of searching successor in case of crash
        predecessor = network.NetworkController.getMySelf().getPredecessor()
        port = predecessor.getPortForTag(ConnectionType.CRASH)
        netUpdate = __initConnection(predecessor.getIp(), port, ConnectionType.CRASH)
        netUpdate.sequentialDelivery(rtMsg, originalIdentity, alreadyReceivedUsers)
    except:
        Logger.info("error during sequentialDelivery")
        Logger.info(traceback.format_exc())
        sendRTMessageErrorCallback(originalIdentity, rtMsg)


def broadcastMessage(rtMsg):
    """This function executes a remote procedure call to the broadcastMessage function (RemoteModel.Crash).
    
    Broadcasts a message to all connected identities
    
    Args:
        rtMsg: The ReliableTransportMessage instance.
    """
    
    try:
        #send broadcast to all of my connected identities
        allConnectedIdentities = network.NetworkController.getMySelf().getAllConnectedIdentities()
        for identity in allConnectedIdentities:
            if identity != None:
                Logger.info("  -> send broadcast to " + identity.getName())
                try:
                    port = identity.getPortForTag(ConnectionType.CRASH)
                    if port != None:
                        netUpdate = __initConnection(identity.getIp(), port, ConnectionType.CRASH)
                        netUpdate.broadcastMessage(rtMsg)
                    else:
                        Logger.info('port is None in broadcastMessage')
                except:
                    #this could happen if it is a crashmessage and this identity is crashed...
                    Logger.info("    - Inner Exception in broadcastMessage (Sender) to " + identity.getName())
            else:
                Logger.info("Identity is None in broadcastMessage")
    except:
        Logger.info("Outlying Exception in broadcastMessage (Sender)")
        Logger.info(traceback.format_exc())
    
    
def callbackCrashBroadcast(crashedIdentity, predecessorOfCrashedIdentity, successorOfCrashPeerFound, sendCount=3):
    """This function executes a remote procedure call to the callbackCrashBroadcast function (RemoteModel.Crash).
    
    Args:
        crashedIdentity: The PeerInfo instance of the crashed peer.
        predecessorOfCrashedIdentity: The receiver of the call (PeerInfo intance)
        successorOfCrashPeerFound: A boolean determining whether the successor of the crashed peer is already found.
        sendCount: Maximum number of retries.
    """
    
    #call callback at original identity of broadcast
    try:
        if predecessorOfCrashedIdentity != None:
            sendCount -= 1
            myIdentity = network.NetworkController.getMySelf().getMyIdentity()
            port = predecessorOfCrashedIdentity.getPortForTag(ConnectionType.CRASH)
            if port != None:
                netUpdate = __initConnection(predecessorOfCrashedIdentity.getIp(), port, ConnectionType.CRASH)
                netUpdate.callbackCrashBroadcast(crashedIdentity, myIdentity, successorOfCrashPeerFound)
            else:
                Logger.info('port none in callbackCrashBroadcast')
        else:
            Logger.info('predecessorOfCrashedIdentity is None in callbackCrashBroadcast')
    except:
        Logger.info("error while callbacking a crash broadcast in sender. count: " + str(sendCount))
        if sendCount > 0:
            networktimer.ResendTimer.addCall([callbackCrashBroadcast, crashedIdentity, predecessorOfCrashedIdentity, successorOfCrashPeerFound, sendCount])


    

######################
## MESSAGE_EXCHANGE ##
######################
        
def hello(isLogin, staticPeerList):
    """This function executes a remote procedure call to the hello function (RemoteModel.MessageExchange).
    
    Tries in the worst case all entries in the static peer list.
    
    Args:
        isLogin: A boolean determining whether the executed authentication is a registration or a login.
        staticPeerList: a list of static peers
    """
    
    KeepAliveTimer.setIsDoingHello(True)
    if staticPeerList == None:
        staticPeerList = XMLController.getStaticPeerList()
    try:
        network.NetworkController.getMySelf().setAuthenticated(False)
        network.NetworkController.getMySelf().setIsLowLevelPeer(True)
        
        if staticPeerList != None and len(staticPeerList) > 0:
            random.shuffle(staticPeerList)
            firstStaticPeer = staticPeerList[0]
            if firstStaticPeer.getPort() != None and firstStaticPeer.getIp() != None:
                myIdentity = network.NetworkController.getMySelf().getMyIdentity()
                myPort = myIdentity.getPortForTag(ConnectionType.MESSAGE_EXCHANGE)
                myIp = myIdentity.getIp()
                if str(myPort) != str(firstStaticPeer.getPort()) or myIp != firstStaticPeer.getIp():
                    KeepAliveTimer.startKeepAliveTimer()
                    newRespPeer = model.PeerModel.PeerInfo(ip=firstStaticPeer.getIp(), ports={ConnectionType.MESSAGE_EXCHANGE : firstStaticPeer.getPort()})
                    network.NetworkController.getMySelf().setResponsibleSuperPeer(newRespPeer)
                
                    myIdentity = network.NetworkController.getMySelf().getMyIdentity()
                    Logger.info("send hello to " + firstStaticPeer.getIp() + " (" + firstStaticPeer.getPort() + ")")
                    messExchange = __initConnection(firstStaticPeer.getIp(), firstStaticPeer.getPort(), ConnectionType.MESSAGE_EXCHANGE)
                    messExchange.hello(myIdentity, isLogin)
                    KeepAliveTimer.setIsDoingHello(False)
                else:
                    raise Exception("Exception", 'wrong static peer')
            else:
                Logger.info('port none in hello')
                KeepAliveTimer.stopKeepAliveTimer()
        else:
            MySignal.emitAuthFinishedSignal(PTPModel.ReturnType.NO_KNOWN_PEER_ONLINE)
            KeepAliveTimer.stopKeepAliveTimer()
    except:
        KeepAliveTimer.stopKeepAliveTimer()
        staticPeerList.pop(0)
        Logger.info("length of staticPeerList=" + str(len(staticPeerList)))
        if len(staticPeerList) > 0:
            networktimer.ResendTimer.addCall([hello, isLogin, staticPeerList])
        else:
            MySignal.emitAuthFinishedSignal(PTPModel.ReturnType.NO_KNOWN_PEER_ONLINE)
        Logger.info('exception during hello in sender')
        Logger.info(traceback.format_exc())
        
        
def forwardPersonalMessage(receiverName, senderName, senderRespIdentity, message, timestamp, crashSend=False):
    """This function executes a remote procedure call to the forwardPersonalMessage function (RemoteModel.MessageExchange).
    
    Args:
        receiverName: the name of the receiver (string)
        senderName: the name of the sender (string)
        senderRespIdentity: the responsible identity of the sender (PeerInfo instance)
        message: the personal message (string)
        timestamp: the timestamp
        crashSend: a boolean determining whether a crash is already executed.
    """
    
    receiverHashedNameLong = long(Hash.hash(receiverName), 16)
    nearestIdentity = network.NetworkController.getMySelf().getNearestIdentity(receiverHashedNameLong)[0]
    try:
        if nearestIdentity != None:
            port = nearestIdentity.getPortForTag(ConnectionType.MESSAGE_EXCHANGE)
            if port != None:
                m = __initConnection(nearestIdentity.getIp(), port, ConnectionType.MESSAGE_EXCHANGE)
                m.forwardPersonalMessage(receiverName, senderName, senderRespIdentity, message, timestamp)
            else:
                Logger.info('port is None in forwardPersonalMessage')
        else:
            Logger.info('nearestIdentity is None in forwardPersonalMessage')
    except:
        Logger.info('exception while sending personal message in sender')
        if crashSend == False:
            logic.CrashLogic.handleCrash(nearestIdentity)
        networktimer.ResendTimer.addCall([forwardPersonalMessage, receiverName, senderName, senderRespIdentity, message, timestamp, True])


def callbackPersonalMessage(receiverRespIdentity, receiverName, senderName, timestamp, status, crashSend=False):
    """This function executes a remote procedure call to the callbackPersonalMessage function (RemoteModel.MessageExchange).
    
    Args:
        receiverRespIdentity: the responsible identity of the receiver (PeerInfo instance)
        receiverName: the name of the receiver (string)
        senderName: the name of the sender (string)
        timestamp: the timestamp
        status: the status 
        crashSend: a boolean determining whether a crash is already executed.
    """
    
    try:
        if receiverRespIdentity != None:
            port = receiverRespIdentity.getPortForTag(ConnectionType.MESSAGE_EXCHANGE)
            if port != None:
                respSearch = __initConnection(receiverRespIdentity.getIp(), port, ConnectionType.MESSAGE_EXCHANGE)
                respSearch.callbackPersonalMessage(receiverRespIdentity, receiverName, senderName, timestamp, status)
            else:
                Logger.info('port none in callbackPersonalMessage')
        else:
            Logger.info('superPeerIdentity is none in callbackPersonalMessage')
    except:
        Logger.info("error while callbacking personal message - crash sent " + str(crashSend))
        if crashSend == False:
            logic.CrashLogic.handleCrash(receiverRespIdentity)
        networktimer.ResendTimer.addCall([callbackPersonalMessage, receiverRespIdentity, receiverName, senderName, timestamp, status, True])


def forwardRingReport(ringReportEntries, fingerValidationDict, backupValidationDict, alreadyReceivedUsers, type, crashSend=False):
    """This function executes a remote procedure call to the forwardRingReport function (RemoteModel.MessageExchange).
    
    Args:
        ringReportEntries: all collected RingReportEntry instances
        fingerValidationDict: a dictinary for validating the fingertables
        backupValidationDict: a dictinary for validating the backups
        alreadyReceivedUsers: a list of peers that already received the message
        type: the report type (defined in RingReportType)
        crashSend: a boolean determining whether a crash is already executed.
    """
    
    try:
        successor = network.NetworkController.getMySelf().getSuccessor()
        if successor != None:
            port = successor.getPortForTag(ConnectionType.MESSAGE_EXCHANGE)
            if port != None:
                messExchange = __initConnection(successor.getIp(), port, ConnectionType.MESSAGE_EXCHANGE)
                messExchange.forwardRingReport(ringReportEntries, fingerValidationDict, backupValidationDict, alreadyReceivedUsers, type)
            else:
                Logger.info('port none in forwardRingReport')
        else:
            Logger.info('identity is none in forwardRingReport')
    except:
        Logger.info("error while forwarding ring report in sender")
        if crashSend == False:
            logic.CrashLogic.handleCrash(successor)
        networktimer.ResendTimer.addCall([forwardRingReport, ringReportEntries, fingerValidationDict, backupValidationDict, alreadyReceivedUsers, type, True])   


def sendKeepAlive(responsibleSuperPeerIdentity, queueType):
    """This function executes a remote procedure call to the keepAlive function (RemoteModel.MessageExchange).
    
    Args:
        responsibleSuperPeerIdentity: the receiver of the call (PeerInfo instance)
        queueType: the type defined by CustodyQueueType
        
    Returns:
        Returns a tuple containing a return type (defined by ReturnType) and a list of CustodyMessage instances.
    """
    
    try:
        if responsibleSuperPeerIdentity != None:
            port = responsibleSuperPeerIdentity.getPortForTag(ConnectionType.MESSAGE_EXCHANGE)
            if port != None:
                myName = network.NetworkController.getMySelf().getMyIdentity().getName()
                messExchange = __initConnection(responsibleSuperPeerIdentity.getIp(), port, ConnectionType.MESSAGE_EXCHANGE)
                return messExchange.keepAlive(myName, queueType)
            else:
                Logger.info('port none in sendKeepAlive')
        else:
            Logger.info('responsibleSuperPeerIdentity None in sendKeepAlive')
    except:
        Logger.info("error while sending keep alive in sender.")
        #don't do anything... this message will be resent in a few seconds anyway
    return (PTPModel.ReturnType.ERROR, None)


def isAlive(identity):
    """This function executes a remote procedure call to the isAlive function (RemoteModel.MessageExchange).
    
    Args:
        identity: the receiver of the call (PeerInfo instance)
        
    Returns:
        Returns False if an error occurred, True otherwise
    """
    
    try:
        if identity != None:
            port = identity.getPortForTag(ConnectionType.MESSAGE_EXCHANGE)
            if port != None:
                messExchange = __initConnection(identity.getIp(), port, ConnectionType.MESSAGE_EXCHANGE)
                return messExchange.isAlive()
            else:
                Logger.info('port none in isAlive')
        else:
            Logger.info('identity None in isAlive')
    except:
        pass
    return False


def sendMediaContent(content, timestamp, receiverRespIdentity, receiverName):
    """This function executes a remote procedure call to the sendMediaContent function (RemoteModel.MessageExchange).
    
    Args:
        content: the transmitted media content.
        timestamp: the timestamp
        receiverRespIdentity: the receiver of the call (PeerInfo instance)
        receiverName: the name of the receiver (string)
    """
    
    try:
        if receiverRespIdentity != None and content != None:
            port = receiverRespIdentity.getPortForTag(ConnectionType.MESSAGE_EXCHANGE)
            if port != None:
                messExchange = __initConnection(receiverRespIdentity.getIp(), port, ConnectionType.MESSAGE_EXCHANGE)
                messExchange.sendMediaContent(content, timestamp, receiverName)
            else:
                Logger.info('port none in sendMediaContent')
        else:
            Logger.info('receiver or content None in sendMediaContent')
    except:
        Logger.info("error while sending media content.")


def pollMediaContentSinceLastSynch(senderIdentity, senderName, membername, group, timestamp, crashSend=False):
    """This function executes a remote procedure call to the pollMediaContentSinceLastSynch function (RemoteModel.MessageExchange).
    
    Args:
        senderIdentity: the sender identity (PeerInfo instance)
        senderName: the name of the sender (string)
        membername: the name of the member (string)
        group: the Group instance
        timestamp: the timestamp
        crashSend: a boolean determining whether a crash is already executed.
    """
    
    memberHashedNameLong = long(Hash.hash(membername), 16)
    nearestIdentity = network.NetworkController.getMySelf().getNearestIdentity(memberHashedNameLong)[0]
    try:
        if nearestIdentity != None:
            port = nearestIdentity.getPortForTag(ConnectionType.MESSAGE_EXCHANGE)
            if port != None:
                groupMngment = __initConnection(nearestIdentity.getIp(), port, ConnectionType.MESSAGE_EXCHANGE)
                groupMngment.pollMediaContentSinceLastSynch(senderIdentity, senderName, membername, group, timestamp)
            else:
                Logger.info('port none in getMediaContentSinceLastSynch')
        else:
            Logger.info('identity None in getMediaContentSinceLastSynch')
    except:
        Logger.info("Error while getting media content since last synch in sender")
        if network.NetworkController.getMySelf().isLowLevelPeer() == False:
            if crashSend == False:
                logic.CrashLogic.handleCrash(nearestIdentity)
            networktimer.ResendTimer.addCall([pollMediaContentSinceLastSynch, senderIdentity, senderName, membername, group, timestamp, True])   


######################
## GROUP_MANAGEMENT ##
######################

def getGroupsWithOwner(originalRespIdentity, originalName, ownername, groupname, displayname, genre, reactType, crashSend=False):
    """This function executes a remote procedure call to the getGroupsWithOwner function (RemoteModel.GroupManagement).
    
    Args:
        originalRespIdentity: The original identity (PeerInfo instance)
        originalName: the name of the original identity (string)
        ownername: the owner name (string)
        groupname: the group name (string)
        displayname: the display name (string)
        genre: the genre (string)
        reactType: determines how to react on the execution.
        crashSend: a boolean determining whether a crash is already executed.
    """
    
    ownerHashedNameLong = long(Hash.hash(ownername), 16)
    nearestIdentity = network.NetworkController.getMySelf().getNearestIdentity(ownerHashedNameLong)[0]
    try:
        if nearestIdentity != None:
            port = nearestIdentity.getPortForTag(ConnectionType.GROUP_MANAGEMENT)
            if port != None:
                groupMngment = __initConnection(nearestIdentity.getIp(), port, ConnectionType.GROUP_MANAGEMENT)
                groupMngment.getGroupsWithOwner(originalRespIdentity, originalName, ownername, groupname, displayname, genre, reactType)
            else:
                Logger.info('port none in getGroupsWithOwner')
        else:
            Logger.info('identity None in getGroupsWithOwner')
    except:
        Logger.info("Error while sending request to get groups with owner in sender")
        if network.NetworkController.getMySelf().isLowLevelPeer() == False:
            if crashSend == False:
                logic.CrashLogic.handleCrash(nearestIdentity)
            networktimer.ResendTimer.addCall([getGroupsWithOwner, originalRespIdentity, originalName, ownername, groupname, displayname, genre, reactType, True])   


def getGroupsWithoutOwner(originalRespIdentity, originalName, alreadyReceivedUsers, groupname, displayname, genre, reactType, crashSend=False):
    """This function executes a remote procedure call to the getGroupsWithoutOwner function (RemoteModel.GroupManagement).
    
    Args:
        originalRespIdentity: The original identity (PeerInfo instance)
        originalName: the name of the original identity (string)
        alreadyReceivedUsers: a list containing all peers that already received the message
        groupname: the group name (string)
        displayname: the display name (string)
        genre: the genre (string)
        reactType: determines how to react on the execution.
        crashSend: a boolean determining whether a crash is already executed.
    """
    
    if network.NetworkController.getMySelf().isLowLevelPeer():
        respIdentityOrSuccessor = network.NetworkController.getMySelf().getResponsibleSuperPeer()
    else:
        respIdentityOrSuccessor = network.NetworkController.getMySelf().getSuccessor()
    try:
        if respIdentityOrSuccessor != None:
            port = respIdentityOrSuccessor.getPortForTag(ConnectionType.GROUP_MANAGEMENT)
            if port != None:
                groupMngment = __initConnection(respIdentityOrSuccessor.getIp(), port, ConnectionType.GROUP_MANAGEMENT)
                groupMngment.getGroupsWithoutOwner(originalRespIdentity, originalName, alreadyReceivedUsers, groupname, displayname, genre, reactType)
            else:
                Logger.info('port none in getGroupsWithoutOwner')
        else:
            Logger.info('identity None in getGroupsWithoutOwner')
    except:
        Logger.info("Error while sending request to get groups without owner in sender")
        if network.NetworkController.getMySelf().isLowLevelPeer() == False:
            if crashSend == False:
                logic.CrashLogic.handleCrash(respIdentityOrSuccessor)
            networktimer.ResendTimer.addCall([getGroupsWithoutOwner, originalRespIdentity, originalName, alreadyReceivedUsers, groupname, displayname, genre, reactType, True])   


def callbackGroups(identity, originalName, groups, reactType, searchFinished, sendCount=3):
    """This function executes a remote procedure call to the callbackGroups function (RemoteModel.GroupManagement).
    
    Args:
        identity: the receiver of the call (PeerInfo instance)
        originalName: the name of the original identity (string)
        groups: a list containing Group instances
        reactType: determines how to react on the execution.
        searchFinished: a boolean that determines whether the search procedure is finished.
        sendCount: Maximum number of retries.
    """
    
    try:
        sendCount -= 1
        if identity != None:
            port = identity.getPortForTag(ConnectionType.GROUP_MANAGEMENT)
            if port != None:
                groupMngment = __initConnection(identity.getIp(), port, ConnectionType.GROUP_MANAGEMENT)
                groupMngment.callbackGroups(originalName, groups, reactType, searchFinished)
            else:
                Logger.info('port none in callbackGroups')
        else:
            Logger.info('identity None in callbackGroups')
    except:
        Logger.info("Error while callbacking groups in sender. sendCount=" + str(sendCount))
        if sendCount == 0:
            logic.CrashLogic.handleCrash(identity)
        if sendCount > 0:
            networktimer.ResendTimer.addCall([callbackGroups, identity, originalName, groups, reactType, searchFinished, sendCount])


def getGroupsForLowLevel(username):
    """This function executes a remote procedure call to the getGroupsForLowLevel function (RemoteModel.GroupManagement).
    
    Args:
        username: the name of the low level peer (string)
        
    Returns:
        Returns a list of Group instances.
    """
    
    respPeer = network.NetworkController.getMySelf().getResponsibleSuperPeer()
    try:
        if respPeer != None:
            port = respPeer.getPortForTag(ConnectionType.GROUP_MANAGEMENT)
            if port != None:
                groupMngment = __initConnection(respPeer.getIp(), port, ConnectionType.GROUP_MANAGEMENT)
                return groupMngment.getGroupsForLowLevel(username)
            else:
                Logger.info('port is None in establishGroupForLowLevel')
        else:
            Logger.info('respPeer is None in establishGroupForLowLevel')
    except:
        Logger.info('exception while establishing group for low level peer at super peer ' + respPeer.getName() + ' in sender')


def getGroupsAndMessageCountForLowLevel(username):
    """This function executes a remote procedure call to the getGroupsAndMessageCountForLowLevel function (RemoteModel.GroupManagement).
    
    Args:
        username: the name of the low level peer (string)
        
    Returns:
        Returns only already processed messages because the not processed once get put into the custody queue
        and the low level peer will process them.
    """
    
    respPeer = network.NetworkController.getMySelf().getResponsibleSuperPeer()
    try:
        if respPeer != None:
            port = respPeer.getPortForTag(ConnectionType.GROUP_MANAGEMENT)
            if port != None:
                groupMngment = __initConnection(respPeer.getIp(), port, ConnectionType.GROUP_MANAGEMENT)
                return groupMngment.getGroupsAndMessageCountForLowLevel(username)
            else:
                Logger.info('port is None in establishGroupForLowLevel')
        else:
            Logger.info('respPeer is None in establishGroupForLowLevel')
    except:
        Logger.info('exception while establishing group for low level peer at super peer ' + respPeer.getName() + ' in sender')
    return None


def establishGroupForLowLevel(group):
    """This function executes a remote procedure call to the establishGroupForLowLevel function (RemoteModel.GroupManagement).
    
    Args:
        group: the Group instance
        
    Returns:
        Returns the group and a return value whether the establishment was successful or not.
    """
    
    respPeer = network.NetworkController.getMySelf().getResponsibleSuperPeer()
    try:
        if respPeer != None:
            port = respPeer.getPortForTag(ConnectionType.GROUP_MANAGEMENT)
            if port != None:
                groupMngment = __initConnection(respPeer.getIp(), port, ConnectionType.GROUP_MANAGEMENT)
                return groupMngment.establishGroupForLowLevel(group)
            else:
                Logger.info('port is None in establishGroupForLowLevel')
        else:
            Logger.info('respPeer is None in establishGroupForLowLevel')
    except:
        Logger.info('exception while establishing group for low level peer at super peer ' + respPeer.getName() + ' in sender')
    return (None, PTPModel.ReturnType.ERROR)


def changeGroupDetailsForLowLevel(group):
    """This function executes a remote procedure call to the changeGroupDetailsForLowLevel function (RemoteModel.GroupManagement).
    
    Args:
        group: the Group instance
        
    Returns:
        Returns whether the update was successful or not.
    """
    
    respPeer = network.NetworkController.getMySelf().getResponsibleSuperPeer()
    try:
        if respPeer != None:
            port = respPeer.getPortForTag(ConnectionType.GROUP_MANAGEMENT)
            if port != None:
                groupMngment = __initConnection(respPeer.getIp(), port, ConnectionType.GROUP_MANAGEMENT)
                return groupMngment.changeGroupDetailsForLowLevel(group)
            else:
                Logger.info('port is None in changeGroupDetailsForLowLevel')
        else:
            Logger.info('respPeer is None in changeGroupDetailsForLowLevel')
    except:
        Logger.info('exception while changing group for low level peer at super peer ' + respPeer.getName() + ' in sender')
    return PTPModel.ReturnType.ERROR


def addMember(ownername, membername, groupname, originalIdentity, crashSend=False):
    """This function executes a remote procedure call to the addMember function (RemoteModel.GroupManagement).
    
    Args:
        ownername: the owner name (string).
        membername: the member name (string).
        groupname: the group name (string).
        originalIdentity: the PeerInfo instance.
        crashSend: a boolean determining whether a crash is already executed.
    """
    
    ownerHashedNameLong = long(Hash.hash(ownername), 16)
    nearestIdentity = network.NetworkController.getMySelf().getNearestIdentity(ownerHashedNameLong)[0]
    try:
        if nearestIdentity != None:
            port = nearestIdentity.getPortForTag(ConnectionType.GROUP_MANAGEMENT)
            if port != None:
                groupMngment = __initConnection(nearestIdentity.getIp(), port, ConnectionType.GROUP_MANAGEMENT)
                groupMngment.addMember(ownername, membername, groupname, originalIdentity)
            else:
                Logger.info('port is None in addMember')
        else:
            Logger.info('nearestIdentity is None in addMember')
    except:
        Logger.info('exception while adding member in sender')
        if network.NetworkController.getMySelf().isLowLevelPeer() == False:
            Logger.info(traceback.format_exc())
            if crashSend == False:
                logic.CrashLogic.handleCrash(nearestIdentity)
            networktimer.ResendTimer.addCall([addMember, ownername, membername, groupname, originalIdentity, True])


def callbackMemberStatusChanged(interestedRespIdentity, interestedName, membername, groupname, ownername, result, status, sendCount=3):
    """This function executes a remote procedure call to the callbackMemberStatusChanged function (RemoteModel.GroupManagement).
    
    Args:
        interestedRespIdentity: the receiver of the call (PeerInfo instance)
        interestedName: or the member name, or the owner name (string).
        membername: the member name (string).
        groupname: the group name (string).
        ownername the owner name (string).
        result: the result
        status: the status
        sendCount: Maximum number of retries.
    """
    
    sendCount -= 1
    try:
        if interestedRespIdentity != None:
            port = interestedRespIdentity.getPortForTag(ConnectionType.GROUP_MANAGEMENT)
            if port != None:
                Logger.info("identity:" + interestedRespIdentity.getName())
                groupMngment = __initConnection(interestedRespIdentity.getIp(), port, ConnectionType.GROUP_MANAGEMENT)
                groupMngment.callbackMemberStatusChanged(interestedName, membername, groupname, ownername, result, status)
            else:
                Logger.info('port is None in callbackMemberStatusChanged')
        else:
            Logger.info('nearestIdentity is None in callbackMemberStatusChanged')
    except:
        Logger.info('exception while callbacking change member status in sender')
        Logger.info(traceback.format_exc())
        if sendCount == 0:
            logic.CrashLogic.handleCrash(interestedRespIdentity)
        if sendCount > 0:
            networktimer.ResendTimer.addCall([callbackMemberStatusChanged, interestedRespIdentity, interestedName, membername, groupname, ownername, result, status, sendCount])


def setMemberStatus(respIdentity, originalName, membername, groupname, ownername, status, crashSend=False):
    """This function executes a remote procedure call to the setMemberStatus function (RemoteModel.GroupManagement).
    
    Args:
        respIdentity: the responsible identity (PeerInfo instance).
        originalName: the name or the original identity (string).
        membername: the member name (string).
        groupname: the group name (string).
        ownername the owner name (string).
        status: the status
        crashSend: a boolean determining whether a crash is already executed.
    """
    
    hashedNameLong = long(Hash.hash(ownername), 16)
    nearestIdentity = network.NetworkController.getMySelf().getNearestIdentity(hashedNameLong)[0]
    try:
        if nearestIdentity != None:
            port = nearestIdentity.getPortForTag(ConnectionType.GROUP_MANAGEMENT)
            if port != None:
                groupMngment = __initConnection(nearestIdentity.getIp(), port, ConnectionType.GROUP_MANAGEMENT)
                groupMngment.setMemberStatus(respIdentity, originalName, membername, groupname, ownername, status)
            else:
                Logger.info('port is None in confirmNewMemberForLowLevel')
        else:
            Logger.info('nearestIdentity is None in confirmNewMemberForLowLevel')
    except:
        Logger.info('exception while setting status of member at peer ' + nearestIdentity.getName() + ' in sender')
        if network.NetworkController.getMySelf().isLowLevelPeer() == False:
            if crashSend == False:
                logic.CrashLogic.handleCrash(nearestIdentity)
            networktimer.ResendTimer.addCall([setMemberStatus, respIdentity, originalName, membername, groupname, ownername, status, True])
    
    
def getGroupsWithMembersToConfirmForLowLevel(llName):
    """This function executes a remote procedure call to the getGroupsWithMembersToConfirmForLowLevel function (RemoteModel.GroupManagement).
    
    Args:
        llName: the name of the low level peer (string)
        
    Returns:
        Returns a list of Group instances, None if an error occurred.
    """
    
    respPeer = network.NetworkController.getMySelf().getResponsibleSuperPeer()
    try:
        if respPeer != None:
            port = respPeer.getPortForTag(ConnectionType.GROUP_MANAGEMENT)
            if port != None:
                groupMngment = __initConnection(respPeer.getIp(), port, ConnectionType.GROUP_MANAGEMENT)
                return groupMngment.getGroupsWithMembersToConfirmForLowLevel(llName)
            else:
                Logger.info('port is None in getMembersToConfirmForLowLevel')
        else:
            Logger.info('respPeer is None in getMembersToConfirmForLowLevel')
    except:
        Logger.info('exception while getting members to confirm for low level peer at responsible peer ' + respPeer.getName() + ' in sender')
    return None

    
def getStoredMessagesForLowLevel(llName):
    """This function executes a remote procedure call to the getStoredMessagesForLowLevel function (RemoteModel.GroupManagement).
    
    Args:
        llName: the name of the low level peer (string)
        
    Returns:
        Returns a list of MemberStatusChangedMessage instances, an empty list if an error occurred.
    """
    
    respPeer = network.NetworkController.getMySelf().getResponsibleSuperPeer()
    try:
        if respPeer != None:
            port = respPeer.getPortForTag(ConnectionType.GROUP_MANAGEMENT)
            if port != None:
                groupMngment = __initConnection(respPeer.getIp(), port, ConnectionType.GROUP_MANAGEMENT)
                return groupMngment.getStoredMessagesForLowLevel(llName)
            else:
                Logger.info('port is None in getStoredMessagesForLowLevel')
        else:
            Logger.info('respPeer is None in getStoredMessagesForLowLevel')
    except:
        Logger.info('exception while getting stored msgs for low level peer at responsible peer ' + respPeer.getName() + ' in sender')
    return []
